package com.songcha.module_bookreader.ui.view.readview

import android.content.Context
import android.graphics.*
import android.graphics.Paint.FontMetrics
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import com.songcha.library_common.util.*

//scroll read flip widget by imitate recyclerview source code,include base function
class CustomScrollWidget
@JvmOverloads
constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0,
    defRes: Int = 0
) : View(context,attrs,defStyle,defRes) {
    private lateinit var mScroller:Scroller
    //recycler,read and write use queue style
    private var mRecyclerViews=ArrayDeque<BitmapView>(2)
    private var mActivityViews=ArrayList<BitmapView>(2)
    private var mScrollOffset=0
    private var mCurrentPage=0
    private var mLastY=0f
    private var mTouchY=0f
    private var mViewWidth=ScreenUtil.getScreenWidth()
    private var mViewHeight=ScreenUtil.getScreenRealHeight()
    private var mFlipDirection=FLIP_DIRECTION_NONE
    private var mTextPaint= Paint()
    private var mTextSize=20f.sp2px()
    private var mFontMetrics=FontMetrics()
    private var mVelocityTracker:VelocityTracker?=null
    private var mScaledMinimumFlingVelocity=0
    private var mScaledMaximumFlingVelocity=0
    //private var mNextBitmap:Bitmap?=null
    private var mLastFlingY=0

    companion object{
        const val FLIP_DIRECTION_NONE=0
        const val FLIP_DIRECTION_UP=1
        const val FLIP_DIRECTION_DOWN=2
        const val MAX_PAGE=30

        class BitmapView(var bitmap:Bitmap,val canvas: Canvas, var srcRect: Rect,var destRect:Rect, var top:Int=0,var bottom:Int=0){
        }
    }

    init {
        initView(context, attrs)
    }

    private fun initView(context: Context, attrs: AttributeSet?){
        //scroll relative prop init
        mScroller=Scroller(context, DecelerateInterpolator())
        mScaledMinimumFlingVelocity= ViewConfiguration.get(context).scaledMinimumFlingVelocity
        mScaledMaximumFlingVelocity= ViewConfiguration.get(context).scaledMaximumFlingVelocity

        mTextPaint=Paint()
        mTextPaint.isAntiAlias=true
        mTextPaint.textSize=mTextSize
        mTextPaint.color= Color.BLACK
        mFontMetrics=FontUtil.getFontMetrics(mTextSize,false)

        //init recycler pool,get view need to via recycler poll
        for(i in 0 until 2){
            val bitmap=Bitmap.createBitmap(mViewWidth,mViewHeight,Bitmap.Config.ARGB_8888)
            val view=BitmapView(bitmap, Canvas(bitmap),
                Rect(0,0,mViewWidth,mViewHeight),Rect(0,0,mViewWidth,mViewHeight),
                0,mViewHeight)
            mRecyclerViews.addFirst(view)
        }
        mActivityViews.clear()
        fillDown(0,0)
    }

    //fill to down
    private fun fillDown(bottomEdge:Int,offset:Int){
        //recycle
        val iterator=mActivityViews.iterator()
        while(iterator.hasNext()){
            val view=iterator.next()
            view.top=view.top+offset
            view.bottom=view.bottom+offset
            view.destRect.top=view.top
            view.destRect.bottom=view.bottom

            if(view.bottom<=0){
                mRecyclerViews.addFirst(view)
                iterator.remove()

                //if not in bounds,not need restore
                if(mFlipDirection==FLIP_DIRECTION_UP){
                    restoreNextPage()
                }
            }
        }
        //generate
        var realBottom=bottomEdge+offset
        while(realBottom<mViewHeight && mActivityViews.size<2){
            val view=mRecyclerViews.first()
            //eraseBitmap(view.canvas)

            val hasNext=nextPage(view.canvas)
            //val tmpBitmap=mNextBitmap
            //mNextBitmap=view.bitmap.copy(Bitmap.Config.ARGB_8888,true)

            if(hasNext){
                view.top=realBottom
                view.bottom=realBottom+view.bitmap.height
                view.destRect.top=view.top
                view.destRect.bottom=view.bottom
                mActivityViews.add(view)
                mRecyclerViews.removeFirst()
                realBottom+=view.bitmap.height

                mFlipDirection=FLIP_DIRECTION_DOWN
            }else{
                //mActivityViews maybe zero
                if(mActivityViews.size==0){
                    mActivityViews.add(view)
                    mRecyclerViews.removeFirst()
                }else{
                    mRecyclerViews.addFirst(view)
                }

                val activity=mActivityViews[0]
                activity.top=0
                activity.bottom=mViewHeight
                activity.destRect.top=activity.top
                activity.destRect.bottom=activity.bottom

                ToastUtil.show("no next page")
                return
            }



        }

    }

    //fill to up
    private fun fillUp(topEdge:Int,offset:Int){
        //recycle
        val iterator=mActivityViews.iterator()

        while (iterator.hasNext()){
            val view=iterator.next()
            view.top=view.top+offset
            view.bottom=view.bottom+offset
            view.destRect.top=view.top
            view.destRect.bottom=view.bottom

            if(view.top>=mViewHeight){
                mRecyclerViews.addFirst(view)
                iterator.remove()

                if(mFlipDirection==FLIP_DIRECTION_DOWN){
                    restorePrePage()
                }
            }
        }
        //generate
        var realTop=topEdge+offset
        while(realTop>0 && mActivityViews.size<2){
            val view=mRecyclerViews.first()
            //change canvas will change bitmap ref at the same time

            //eraseBitmap(view.canvas)
            val hasPre=prePage(view.canvas)

            //var tmpBitmap=mNextBitmap
            //mNextBitmap=view.bitmap.copy(Bitmap.Config.ARGB_8888,true)

            if(hasPre){
                view.top=realTop-view.bitmap.height
                view.bottom=realTop
                view.destRect.top=view.top
                view.destRect.bottom=view.bottom
                mActivityViews.add(0,view)
                mRecyclerViews.removeFirst()
                realTop-=view.bitmap.height
                mFlipDirection=FLIP_DIRECTION_UP
            }else{
                //mActivityViews maybe zero
                //restore last bitmap
                //should ensure recyclers and activitys only has one
                if(mActivityViews.size==0){
                    mActivityViews.add(0,view)
                    mRecyclerViews.removeFirst()
                }else{
                    mRecyclerViews.addFirst(view)
                }

                val activity=mActivityViews[0]
                activity.top=0
                activity.bottom=mViewHeight
                activity.destRect.top=activity.top
                activity.destRect.bottom=activity.bottom

                ToastUtil.show("no pre page")
                return
            }

        }

    }

    private fun eraseBitmap(canvas: Canvas){
        if(mCurrentPage%2==0)
            canvas.drawColor(Color.RED)
        else
            canvas.drawColor(Color.GREEN)
    }

    private fun drawText(canvas: Canvas){
        val textBounds=FontUtil.getTextBounds(mCurrentPage.toString(),mTextSize,false)
        canvas.drawText(mCurrentPage.toString(),(mViewWidth-textBounds!!.width())/2f,mViewHeight/2f-(mFontMetrics.ascent)/2,mTextPaint)
    }

    private fun restorePrePage(){
        mCurrentPage--
    }

    private fun restoreNextPage(){
        mCurrentPage++
    }

    private fun nextPage(canvas: Canvas):Boolean{
        if(mCurrentPage>=MAX_PAGE) return false
        mCurrentPage++
        eraseBitmap(canvas)
        drawText(canvas)
        return true
    }

    private fun prePage(canvas: Canvas):Boolean{
        if(mCurrentPage<=1) return false
        mCurrentPage--
        eraseBitmap(canvas)
        drawText(canvas)
        return true
    }

    private fun fillPage(){
        if(mScrollOffset>0){
            //pull down
            if(mActivityViews.size==0) return
            fillUp(mActivityViews[0].top, mScrollOffset)
        }else if(mScrollOffset<0){
            //pull up
            if(mActivityViews.size==0) return
            fillDown(mActivityViews[mActivityViews.size-1].bottom,mScrollOffset)
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if(mVelocityTracker==null){
            mVelocityTracker=VelocityTracker.obtain()
        }
        mVelocityTracker!!.addMovement(event)
        when(event.action){
            MotionEvent.ACTION_DOWN->{
                mLastY=event.y
                mTouchY=event.y
            }
            MotionEvent.ACTION_MOVE->{
                mScrollOffset=(event.y-mTouchY).toInt()
                mTouchY=event.y
                fillPage()
                postInvalidate()
            }
            MotionEvent.ACTION_UP->{

                startAnimation()

                try{
                    mVelocityTracker?.recycle()
                    mVelocityTracker=null
                }catch (e:Exception){
                    e.printStackTrace()
                }
            }
            MotionEvent.ACTION_CANCEL->{
                mLastY=0f
                mTouchY=0f
                try{
                    mVelocityTracker?.recycle()
                    mVelocityTracker=null
                }catch (e:Exception){
                    e.printStackTrace()
                }
            }
        }

        return super.onTouchEvent(event)

    }


    private fun startAnimation(){
        if(mVelocityTracker==null) return

        mVelocityTracker!!.computeCurrentVelocity(1000,mScaledMaximumFlingVelocity.toFloat())
        var yVelocity=mVelocityTracker!!.yVelocity.toInt()

        if (Math.abs(yVelocity) < mScaledMinimumFlingVelocity) {
            yVelocity = 0
        } else {
            yVelocity = Math.max(-mScaledMaximumFlingVelocity, Math.min(yVelocity, mScaledMaximumFlingVelocity))
        }
        //LogUtil.log("startAnimation",yVelocity)
        mLastFlingY=0
        mScroller.fling(0, 0, 0, yVelocity
            , Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE)
        invalidate()

    }


    private fun computeScrollOffset(){
        if (mScroller.computeScrollOffset()) {
            val y=mScroller.currY

            mScrollOffset=y-mLastFlingY
            mLastFlingY=y
            //LogUtil.log("computeScroll",mScroller.currY,mScroller.finalY)
            //mTouchY=y.toFloat()
            fillPage()
            postInvalidate()
        }
    }

    val flingRunnable= Runnable {
        computeScrollOffset()
    }

    override fun computeScroll() {
        super.computeScroll()
        flingRunnable.run()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //var i=0
        for(item in mActivityViews){
            //LogUtil.log("aaa",i,mCurrentPage,item.destRect.top,item.destRect.bottom)
            //i++
            canvas.drawBitmap(item.bitmap,item.srcRect,item.destRect,null)
        }

    }



}